package org.budo.support.lang.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.budo.support.assertion.Assert;
import org.budo.support.mvcs.Mvcs;
import org.budo.support.slf4j.Slf4j;
import org.slf4j.Logger;

/**
 * @author fuqinqin
 */
public class BeanUtil {
    private static Logger log = Slf4j.getLogger();

    /**
     * 对象拷贝
     */
    public static <T> T copyProperties(Object source, Class<T> clazz) {
        return copyProperties(source, clazz, null);
    }

    public static <T> T copyProperties(Object source, Class<T> clazz, Class<? extends Annotation> annotation) {
        if (null == source || null == clazz) {
            log.error("#31 copyProperties, source=" + source + ", clazz=" + clazz);
            return null;
        }

        try {
            return copyProperties_1(source, clazz, annotation);
        } catch (InstantiationException e) {
            log.error("#67 copyProperties error", e);
            return null;
        } catch (IllegalAccessException e) {
            log.error("#69 copyProperties error", e);
            return null;
        }
    }

    private static <T> T copyProperties_1(Object source, Class<T> clazz, Class<? extends Annotation> annotation) throws InstantiationException, IllegalAccessException {
        T target = clazz.newInstance();

        return copyProperties_2(source, target, annotation);
    }

    private static <T> T copyProperties_2(Object source, T target, Class<? extends Annotation> annotation) {
        boolean allSatisfied = false;
        if (annotation == null) {
            allSatisfied = true;
        } else if (ReflectUtil.hasAnnotationPresent(target.getClass(), annotation)) {
            allSatisfied = true;
        }

        List<Field> allFields = ReflectUtil.getAllFields(target.getClass());
        if (CollectionUtil.isEmpty(allFields)) {
            return (T) target;
        }

        Set<String> sourceAttribute = ReflectUtil.getAllAttribute(source.getClass());

        String fieldName = null;
        for (Field field : allFields) {
            fieldName = field.getName();

            // 排除被 static 和 final 关键字修饰的属性
            if (ReflectUtil.isStatic(field) || ReflectUtil.isFinal(field)) {
                continue;
            }

            if (!sourceAttribute.contains(fieldName)) {
                continue;
            }

            if (allSatisfied || ReflectUtil.hasAnnotationPresent(field, annotation)) {
                Object value = ReflectUtil.invokeGet(source, fieldName);
                ReflectUtil.invokeSet(target, field, value);
            }
        }

        return target;
    }

    /**
     * 集合拷贝
     * 
     * @param sourceList  源数据
     * @param targetClass 返回的数据类型
     * @param annotation  用户自定义注解，注解可以作用于类或者属性，如果作用于类，则全部属性拷贝；如果作用于部分属性，则只拷贝部分属性
     */
    public static <T> List<T> copyList(Collection<? extends Object> sourceList, Class<T> targetClass, Class<? extends Annotation> annotation) {
        List<T> targetList = new ArrayList<>();
        if (CollectionUtil.isEmpty(sourceList)) {
            return targetList;
        }

        T each;
        for (Object source : sourceList) {
            each = copyProperties(source, targetClass, annotation);
            targetList.add(each);
        }

        return targetList;
    }

    public static <T> List<T> copyList(Collection<?> sourceList, Class<T> targetClass) {
        return copyList(sourceList, targetClass, null);
    }

    /**
     * 将对象转换成Map
     */
    public static <T> Map<String, Object> bean2Map(T source) {
        return bean2Map(source, null);
    }

    public static <T> Map<String, Object> bean2Map(T source, Class<? extends Annotation> annotation) {
        Assert.notNull(source, "source must not be null");

        Map<String, Object> result = new HashMap<>();

        Class<?> clazz = source.getClass();
        boolean allSatisfied = false;
        if (annotation == null) {
            allSatisfied = true;
        } else if (ReflectUtil.hasAnnotationPresent(clazz, annotation)) {
            allSatisfied = true;
        }

        List<Field> allFields = ReflectUtil.getAllFields(clazz);
        if (CollectionUtil.isEmpty(allFields)) {
            return result;
        }

        String fieldName = null;
        for (Field field : allFields) {
            fieldName = field.getName();

            // 排除被 static 和 final 关键字修饰的属性
            if (ReflectUtil.isStatic(field) || ReflectUtil.isFinal(field)) {
                continue;
            }

            if (allSatisfied || ReflectUtil.hasAnnotationPresent(field, annotation)) {
                Object obj = ReflectUtil.invokeGet(source, fieldName);
                if (obj != null) {
                    result.put(fieldName, obj);
                }
            }
        }

        return result;
    }

    public static <T> List<T> listToEntity(Collection input, Class<T> type) {
        if (null == input) {
            log.error("#163 listToEntity, input=" + input + ", type=" + type);
            return null;
        }

        List<T> list = new ArrayList<>();
        for (Object each : input) {
            if (ReflectUtil.isPrimitiveTypes(type)) {
                Object _value = null != each && each instanceof Map ? ((Map) each).values().iterator().next() : each; // 如果 map 就 取第一个 否则就本身

                T value = (T) ReflectUtil.toType(_value, type); // 数字 时间 等 原始类型
                list.add(value);
                continue;
            }

            T entity = (T) mapToEntity((Map) each, type);
            list.add(entity);
        }

        return list;
    }

    public static <T> T mapToEntity(Map<String, Object> map, Class<?> type) {
        if (null == map || null == type) {
            log.error("#186 mapToEntity, map=" + map + ", type=" + type + ", handler=" + Mvcs.getHandler());

            return null;
        }

        if (type.isEnum()) {
            log.error("#193 mapToEntity, map=" + map + ", type=" + type + ", handler=" + Mvcs.getHandler());
            return null;
        }

        if (type.isInterface() && !Map.class.isAssignableFrom(type)) {
            log.error("#198 mapToEntity, map=" + map + ", type=" + type + ", handler=" + Mvcs.getHandler());
            return null;
        }

        Object entityInstance = null;
        if (Map.class.isAssignableFrom(type)) {
            entityInstance = ReflectUtil.newMap(type);
        } else {
            entityInstance = ReflectUtil.newInstance(type); // 返回对象
        }

        Set<Entry<String, Object>> entrySet = map.entrySet();
        for (Entry<String, Object> entry : entrySet) {
            String fieldName = entry.getKey();
            Object value = entry.getValue();

            // map 直接 put
            if (entityInstance instanceof Map) {
                ((Map) entityInstance).put(fieldName, value);
                continue;
            }

            // 普通对象 通过属性
            Field field = ReflectUtil.findFieldCached(entityInstance.getClass(), fieldName);
            if (null == field) {
                log.debug("#173 mapToEntity, field={}, entity={}, fieldName={}, map={}", field, entityInstance, fieldName, map);
                continue;
            }

            try {
                Object fieldValue = ReflectUtil.toType(value, field.getType());
                ReflectUtil.setFieldValue(entityInstance, field, fieldValue);
            } catch (Throwable e) {
                throw new IllegalArgumentException("#208 fieldName=" + fieldName + ", value=" + value + ", field=" + field + ", value=" + value + ", map=" + map, e);
            }
        }

        return (T) entityInstance;
    }
}
